package org.tamal.classinfo;

import java.io.DataInputStream;
import java.io.IOException;

/**
 * This class represents raw byte code of a method.
 * @author Tamal Kanti Nath
 */
public final class Code {

    private final int maxStack;
    private final int maxLocals;
    private final int codeLength;
    private final ExceptionEntry[] exception;
    private final Attribute[] attributes;
    private final Instruction[] instruction;

    /**
     * Creates code structure from the given data input stream.
     * @param in The data Input Stream
     * @param cls The ClassInfo reference
     * @throws IOException exception
     * @see Attribute
     */
    Code(DataInputStream in, ClassInfo cls) throws IOException {
        maxStack = in.readUnsignedShort();
        maxLocals = in.readUnsignedShort();
        codeLength = in.readInt();
        instruction = Instruction.readInstructions(in, codeLength);
        exception = ExceptionEntry.readExceptionTable(in, cls);
        attributes = Attribute.readAttributes(in, cls);
    }

    /**
     * Returns maxStack.
     * @return the maxStack
     */
    public int getMaxStack() {
        return maxStack;
    }

    /**
     * Returns maxLocals.
     * @return the maxLocals
     */
    public int getMaxLocals() {
        return maxLocals;
    }

    /**
     * Returns instruction array.
     * @return the instruction
     */
    public Instruction[] getInstruction() {
        return instruction;
    }

    /**
     * Returns exception.
     * @return the exception
     */
    public ExceptionEntry[] getException() {
        return exception;
    }

    /**
     * Returns attributes.
     * @return the attributes
     */
    public Attribute[] getAttributes() {
        return attributes;
    }

    /**
     * Return the cyclomatic complexity of the code.
     * @return the complexity
     */
    public int getCyclomaticComplexity() {
        int count = 0;
        for (Instruction i : instruction) {
            switch (i.getOpcode()) {
            case Instruction.IFEQ:
            case Instruction.IFNE:
            case Instruction.IFLT:
            case Instruction.IFGE:
            case Instruction.IFGT:
            case Instruction.IFLE:
            case Instruction.IF_ICMPEQ:
            case Instruction.IF_ICMPNE:
            case Instruction.IF_ICMPLT:
            case Instruction.IF_ICMPGE:
            case Instruction.IF_ICMPGT:
            case Instruction.IF_ICMPLE:
            case Instruction.IF_ACMPEQ:
            case Instruction.IF_ACMPNE:
            case Instruction.IRETURN:
            case Instruction.LRETURN:
            case Instruction.FRETURN:
            case Instruction.DRETURN:
            case Instruction.ARETURN:
            case Instruction.RETURN:
            case Instruction.ATHROW:
                ++count;
                break;
            case Instruction.LOOKUPSWITCH:
                count += i.getOffsets().length;
                break;
            case Instruction.TABLESWITCH:
                int defaults = 0;
                for (int t : i.getOffsets()) {
                    if (i.getDefault() == t) {
                        ++defaults;
                    }
                }
                count += i.getOffsets().length - defaults;
                break;
            default:
                break;
            }
        }
        return count;
    }
}
